1   /*
2    * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /* @test
25   * @bug 4770745 6234507
26   * @summary test a variety of zip file entries
27   * @author Martin Buchholz
28   */
29  
30  import java.util.*;
31  import java.util.zip.*;
32  import java.util.jar.*;
33  import java.io.*;
34  
35  public class Assortment {
36      static int passed = 0, failed = 0;
37  
38      static void fail(String msg) {
39          failed++;
40          new Exception(msg).printStackTrace();
41      }
42  
43      static void unexpected(Throwable t) {
44          failed++;
45          t.printStackTrace();
46      }
47  
48      static void check(boolean condition, String msg) {
49          if (! condition)
50              fail(msg);
51      }
52  
53      static void check(boolean condition) {
54          check(condition, "Something's wrong");
55      }
56  
57      private static class Entry {
58          private String name;
59          private int    method;
60          private byte[] data;
61          private byte[] extra;
62          private String comment;
63  
64          Entry(String name,
65                int    method,
66                byte[] data,
67                byte[] extra,
68                String comment) {
69              this.name    = name;
70              this.method  = method;
71              this.data    = data;
72              this.extra   = extra;
73              this.comment = comment;
74          }
75  
76          void write(ZipOutputStream s) throws Exception {
77              ZipEntry e = new ZipEntry(name);
78              CRC32 crc32 = new CRC32();
79              e.setMethod(method);
80              if (method == ZipEntry.STORED) {
81                  e.setSize(data == null ? 0 : data.length);
82                  crc32.reset();
83                  if (data != null) crc32.update(data);
84                  e.setCrc(crc32.getValue());
85              } else {
86                  e.setSize(0);
87                  e.setCrc(0);
88              }
89              if (comment != null) e.setComment(comment);
90              if (extra   != null) e.setExtra(extra);
91              s.putNextEntry(e);
92              if (data != null) s.write(data);
93          }
94  
95          byte[] getData(ZipFile f, ZipEntry e) throws Exception {
96              byte[] fdata = new byte[(int)e.getSize()];
97              InputStream is = f.getInputStream(e);
98              is.read(fdata);
99              return fdata;
100         }
101 
102         void verify(ZipFile f) throws Exception {
103             ZipEntry e = f.getEntry(name);
104             byte[] data  = (this.data == null) ? new byte[]{} : this.data;
105             byte[] extra = (this.extra != null && this.extra.length == 0) ?
106                 null : this.extra;
107             check(name.equals(e.getName()));
108             check(method == e.getMethod());
109             check((((comment == null) || comment.equals(""))
110                    && (e.getComment() == null))
111                   || comment.equals(e.getComment()));
112             check(Arrays.equals(extra, e.getExtra()));
113             check(Arrays.equals(data, getData(f, e)));
114             check(e.getSize() == data.length);
115             check((method == ZipEntry.DEFLATED) ||
116                   (e.getCompressedSize() == data.length));
117         }
118 
119         void verify(JarInputStream jis) throws Exception {
120             // JarInputStream "automatically" reads the manifest
121             if (name.equals("meta-iNf/ManIfEst.Mf"))
122                 return;
123             ZipEntry e = jis.getNextEntry();
124 
125             byte[] data = (this.data == null) ? new byte[]{} : this.data;
126             byte[] otherData = new byte[data.length];
127             jis.read(otherData);
128             check(Arrays.equals(data, otherData));
129 
130             byte[] extra = (this.extra != null && this.extra.length == 0) ?
131                 null : this.extra;
132             check(Arrays.equals(extra, e.getExtra()));
133 
134             check(name.equals(e.getName()));
135             check(method == e.getMethod());
136             check(e.getSize() == -1 || e.getSize() == data.length);
137             check((method == ZipEntry.DEFLATED) ||
138                   (e.getCompressedSize() == data.length));
139 
140         }
141     }
142 
143     private static int uniquifier = 86;
144     private static String uniquify(String name) {
145         return name + (uniquifier++);
146     }
147 
148     private static byte[] toBytes(String s) throws Exception {
149         return s.getBytes("UTF-8");
150     }
151 
152     private static byte[] toExtra(byte[] bytes) throws Exception {
153         if (bytes == null) return null;
154         // Construct a fake extra field with valid header length
155         byte[] v = new byte[bytes.length + 4];
156         v[0] = (byte) 0x47;
157         v[1] = (byte) 0xff;
158         v[2] = (byte) bytes.length;
159         v[3] = (byte) (bytes.length << 8);
160         System.arraycopy(bytes, 0, v, 4, bytes.length);
161         return v;
162     }
163 
164     private static Random random = new Random();
165 
166     private static String makeName(int length) {
167         StringBuilder sb = new StringBuilder(length);
168         for (int i = 0; i < length; i++)
169             sb.append((char)(random.nextInt(10000)+1));
170         return sb.toString();
171     }
172 
173     public static void main(String[] args) throws Exception {
174         File zipName = new File("x.zip");
175         int[]    methods  = {ZipEntry.STORED, ZipEntry.DEFLATED};
176         String[] names    = {makeName(1), makeName(160), makeName(9000)};
177         byte[][] datas    = {null, new byte[]{}, new byte[]{'d'}};
178         byte[][] extras   = {null, new byte[]{}, new byte[]{'e'}};
179         String[] comments = {null, "", "c"};
180 
181         List<Entry> entries = new ArrayList<Entry>();
182 
183         // Highly unusual manifest
184         entries.add(new Entry("meta-iNf/ManIfEst.Mf",
185                               ZipEntry.STORED,
186                               toBytes("maNiFest-VeRsIon: 1.0\n"),
187                               toExtra(toBytes("Can manifests have extra??")),
188                               "Can manifests have comments??"));
189 
190         // The emptiest possible entry
191         entries.add(new Entry("", ZipEntry.STORED,   null, null, ""));
192 
193         for (String name : names)
194             for (int method : methods)
195                 for (byte[] data : datas) // datae??
196                     for (byte[] extra : extras)
197                         for (String comment : comments)
198                             entries.add(new Entry(uniquify(name), method, data,
199                                                   toExtra(extra), comment));
200 
201         //----------------------------------------------------------------
202         // Write zip file using ZipOutputStream
203         //----------------------------------------------------------------
204         try (FileOutputStream fos = new FileOutputStream(zipName);
205              ZipOutputStream zos = new ZipOutputStream(fos))
206         {
207             for (Entry e : entries)
208                 e.write(zos);
209         }
210 
211         //----------------------------------------------------------------
212         // Verify zip file contents using JarFile class
213         //----------------------------------------------------------------
214         JarFile f = new JarFile(zipName);
215 
216         check(f.getManifest() != null);
217 
218         for (Entry e : entries)
219             e.verify(f);
220 
221         f.close();
222 
223         //----------------------------------------------------------------
224         // Verify zip file contents using JarInputStream class
225         //----------------------------------------------------------------
226         JarInputStream jis = new JarInputStream(
227             new FileInputStream(zipName));
228 
229         // JarInputStream "automatically" reads the manifest
230         check(jis.getManifest() != null);
231 
232         for (Entry e : entries)
233             e.verify(jis);
234 
235         jis.close();
236 
237 //      String cmd = "unzip -t " + zipName.getPath() + " >/dev/tty";
238 //      new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd}).start().waitFor();
239 
240         zipName.deleteOnExit();
241 
242         System.out.printf("passed = %d, failed = %d%n", passed, failed);
243         if (failed > 0) throw new Exception("Some tests failed");
244     }
245 }